<template>
  <div class="tree-graph-container">
    <div class="flex">
      <el-button @click="test12">connector：链接器——normal：默认连接器</el-button>
      <el-button @click="test13">connector：链接器——smooth：平滑连接器</el-button>
      <el-button @click="test14">connector：链接器——rounded：圆角连接器</el-button>
      <el-button @click="test15">connector：链接器——jumpover：跳线连接器</el-button>
      <el-button @click="test16">connector：链接器——自定义连接器</el-button>
      <el-button @click="busRoutes">公交路线</el-button>
    </div>

    <div class="flex flex-row">
      <!-- 1. x6 容器 -->
      <div id="x6Container" ref="containerRef"></div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref, shallowRef } from "vue";
import { Graph, Point, Path } from "@antv/x6";
import { Export } from "@antv/x6-plugin-export";

let graph: Graph | null = null;

// x6 图的实例
const graphInstance = shallowRef();

const containerRef = ref();

// 注册自定义链接器
interface WobbleArgs {
  spread?: number;
  raw?: boolean;
}
Graph.registerConnector(
  "wobble",
  (sourcePoint, targetPoint, vertices, args: WobbleArgs) => {
    const spread = args.spread || 20;
    const points = [...vertices, targetPoint].map(p => Point.create(p));
    let prev = Point.create(sourcePoint);
    const path = new Path();
    path.appendSegment(Path.createSegment("M", prev));

    for (let i = 0, n = points.length; i < n; i += 1) {
      const next = points[i];
      const distance = prev.distance(next);
      let d = spread;

      while (d < distance) {
        const current = prev.clone().move(next, -d);
        current.translate(Math.floor(7 * Math.random()) - 3, Math.floor(7 * Math.random()) - 3);
        path.appendSegment(Path.createSegment("L", current));
        d += spread;
      }

      path.appendSegment(Path.createSegment("L", next));
      prev = next;
    }

    return path;
  },
  true
);

const test12 = () => {
  graph.addEdge({
    source: { x: 1000, y: 100 },
    target: { x: 1000, y: 500 },
    vertices: [
      { x: 1050, y: 200 },
      { x: 1050, y: 300 }
    ],
    router: "orth",
    // 系统的默认连接器，将起点、路由点、终点通过直线按顺序连接。
    connector: "normal"
    // 或
    // connector: {
    //   name: 'normal',
    // },
  });
};

const test13 = () => {
  graph.addEdge({
    source: { x: 1050, y: 100 },
    target: { x: 1050, y: 500 },
    vertices: [
      { x: 1100, y: 200 },
      { x: 1100, y: 300 }
    ],
    router: "orth",
    // 平滑连接器，通过三次贝塞尔链接起点、路由点和终点。
    connector: "smooth"
  });
};

const test14 = () => {
  graph.addEdge({
    source: { x: 1100, y: 100 },
    target: { x: 1100, y: 500 },
    vertices: [
      { x: 1150, y: 200 },
      { x: 1150, y: 300 }
    ],
    router: "orth",
    //圆角连接器，将起点、路由点、终点通过直线按顺序连接，并在线段连接处通过圆弧连接（倒圆角）。
    connector: "rounded"
  });
};

const test15 = () => {
  graph.addEdge({
    source: { x: 800, y: 300 },
    target: { x: 1400, y: 300 },
    // vertices: [
    //   { x: 1150, y: 200 },
    //   { x: 1150, y: 300 },
    // ],
    router: "orth",
    // 跳线连接器，用直线连接起点、路由点和终点，并在边与边的交叉处用跳线符号链接。
    connector: {
      name: "jumpover",
      args: {
        type: "arc", // 'arc' | 'gap' | 'cubic' 跳线类型。
        size: 5, // 跳线大小。
        radius: 0, // 倒角半径。
        raw: false // 是否返回一个 Path 对象，默认值为 false 返回序列化后的字符串
      }
    }
  });
};

const test16 = () => {
  graph.addEdge({
    source: { x: 1200, y: 100 },
    target: { x: 1200, y: 500 },
    vertices: [
      { x: 1250, y: 200 },
      { x: 1250, y: 300 }
    ],
    router: "orth",
    // 跳线连接器，用直线连接起点、路由点和终点，并在边与边的交叉处用跳线符号链接。
    connector: {
      name: "wobble",
      args: { spread: 16 }
    }
  });
};

const busRoutes = () => {
  graph.addEdge({
    source: { x: 100, y: 50 },
    target: { x: 1400, y: 50 },
    labels: [
      {
        markup: [
          {
            tagName: "circle",
            selector: "dot"
          },
          {
            tagName: "text",
            selector: "txt"
          }
        ],
        attrs: {
          txt: {
            fill: "#7c68fc",
            textAnchor: "middle",
            textVerticalAnchor: "middle",
            refX: 0,
            refY: 20,
            text: "始发站"
          },
          dot: {
            fill: "#fff",
            stroke: "red",
            strokeWidth: 1,
            r: 5,
            cx: 0,
            cy: 0
          }
        },
        position: {
          distance: 0 // 位置
        }
      },
      {
        markup: [
          {
            tagName: "circle",
            selector: "dot"
          },
          {
            tagName: "text",
            selector: "txt"
          }
        ],
        attrs: {
          txt: {
            fill: "#7c68fc",
            textAnchor: "middle",
            textVerticalAnchor: "middle",
            refX: 0,
            refY: 20,
            text: "站点A"
          },
          dot: {
            fill: "#fff",
            stroke: "#000",
            strokeWidth: 1,
            r: 5,
            cx: 0,
            cy: 0
          }
        },
        position: {
          distance: 0.2 // 位置
        }
      },
      {
        markup: [
          {
            tagName: "circle",
            selector: "dot"
          },
          {
            tagName: "text",
            selector: "txt"
          }
        ],
        attrs: {
          txt: {
            fill: "#7c68fc",
            textAnchor: "middle",
            textVerticalAnchor: "middle",
            refX: 0,
            refY: 20,
            text: "站点B（车辆当前位置）"
          },
          dot: {
            fill: "green",
            stroke: "#000",
            strokeWidth: 1,
            r: 5,
            cx: 0,
            cy: 0
          }
        },
        position: {
          distance: 0.5 // 位置
        }
      },
      {
        markup: [
          {
            tagName: "circle",
            selector: "dot"
          },
          {
            tagName: "text",
            selector: "txt"
          }
        ],
        attrs: {
          txt: {
            fill: "#7c68fc",
            textAnchor: "middle",
            textVerticalAnchor: "middle",
            refX: 0,
            refY: 20,
            text: "终点站"
          },
          dot: {
            fill: "#fff",
            stroke: "red",
            strokeWidth: 1,
            r: 5,
            cx: 0,
            cy: 0
          }
        },
        position: {
          distance: 1 // 位置
        }
      }
    ],
    attrs: {
      line: {
        sourceMarker: "",
        targetMarker: ""
      }
    }
  });
};

// 1.初始化数据
const originalData = ref({
  // 节点
  nodes: [],
  // 边
  edges: []
});
// 2.初始化画布
const init = () => {
  // 初始化 x6 图背景板
  // 创建一个新的 Graph 实例
  graph = new Graph({
    // 指定 Graph 的容器元素
    container: document.getElementById("x6Container")!,

    // 自动调整 Graph 的大小以适应容器
    autoResize: true,

    // 设置背景颜色
    background: {
      color: "#fffbe6" // 设置画布背景颜色
    },
    // 网格设置
    grid: {
      visible: true, // 渲染网格背景
      size: 10, // 网格大小 10px
      type: "doubleMesh",
      args: [
        {
          color: "#eee", // 主网格线颜色
          thickness: 1 // 主网格线宽度
        },
        {
          color: "#ddd", // 次网格线颜色
          thickness: 1, // 次网格线宽度
          factor: 4 // 主次网格线间隔
        }
      ]
    },

    // 滚动设置
    scroller: {
      enabled: false, // 启用滚动
      pannable: true // 允许通过拖动来平移视图
    },

    // 是否启用缩略图（小地图）
    minimap: false, // 禁用缩略图

    // 鼠标滚轮设置
    mousewheel: {
      enabled: true, // 启用鼠标滚轮缩放
      modifiers: ["ctrl", "meta"] // 可以通过按下 Ctrl 或 Meta 键来启用缩放
    },

    // 平移设置
    panning: {
      enabled: true, // 启用平移功能
      modifiers: "space" // 需要按住 space 键才能进行拖拽平移 大空格键
    },

    // 交互设置(拖拽移动)
    interacting: {
      nodeMovable: true
    },

    // 拖拽设置
    dragging: {
      enabled: true, // 启用拖拽功能
      ghost: false // 禁用拖拽时的虚影效果
    },

    // 当源/目标是画布上的点时，需要开启 allowBlank 选项（默认已经开启）才能生效
    connecting: {
      allowBlank: false
    }
  });

  // 渲染
  graph.fromJSON(originalData.value);
  graph.zoomToFit(); // 缩放
  graph.scale(0.7); // 设置初始缩放比例，例如 0.7 表示缩小到 70%
  // graph.centerContent(); // 居中
  graph.use(new Export()); // 加载导出插件
  graphInstance.value = graph; // 保存图实例
};

onMounted(() => {
  init(); // 初始化树数据
});
</script>

<style scoped>
#x6Container {
  flex: 1;
  width: 100%;
  height: 100vh;
  position: relative;
}
</style>
